/*
 *  disp.c
 *  StringSearch
 *
 *  Created by Mohammad H. Afrasiabi on 09-11-16.
 *  Copyright 2009 UBC. All rights reserved.
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <mpi.h>

#include "disp.h"

// Directives
#define CHECK_MEMORY_ALLOCATION(__M) if (__M == 0) return -1;


MPI_Comm			COMM_DUP;

static int			g_chunkSize;
static int			g_overlapSize;
static int			g_textSize;
static int			g_mpi_rank;
static int			g_mpi_size;

static char*		g_chunkBuffer;
static char*		g_textBuffer;
static int*			g_results;


static int*			p_displs;
static int*			p_sendcounts;
static int*			p_partialResult;
static int			p_startChunkIndex;


Disp_Iterator_Type	g_last_iterator;
	

int Disp_Init(char ** textBuffer, int textSize, int chunkSize, int overlapSize)
{
	
	int result	= MPI_Comm_dup(MPI_COMM_WORLD, &COMM_DUP);
	int temp	= -1;
	
	MPI_Comm_rank(COMM_DUP, &g_mpi_rank);
	MPI_Comm_size (COMM_DUP, &g_mpi_size);
	
	g_textSize		= textSize;
	g_chunkSize		= chunkSize;
	
	p_sendcounts	= calloc(g_mpi_size, sizeof(int));
	p_displs		= calloc(g_mpi_size, sizeof(int));
	
	CHECK_MEMORY_ALLOCATION(p_sendcounts)
	CHECK_MEMORY_ALLOCATION(p_displs)
	
	
	g_overlapSize	= overlapSize;
	
	Disp_Sync_Int(&g_overlapSize);
	Disp_Sync_Int(&g_chunkSize);
	Disp_Sync_Int(&g_textSize);
	
	if (Disp_Get_Rank() == ROOT)
	{
		g_textBuffer	= *textBuffer	= malloc(g_textSize + (g_chunkSize * g_mpi_size) * sizeof(char));
		g_results		= malloc(PARTIAL_RESULT_SIZE * g_mpi_size * sizeof(int));
		
		CHECK_MEMORY_ALLOCATION(g_textBuffer)
		CHECK_MEMORY_ALLOCATION(g_results)
		
		memset_pattern4(g_results, (void *) &temp, PARTIAL_RESULT_SIZE * g_mpi_size * sizeof(int));
	}
	
	
	g_chunkBuffer		= malloc(g_chunkSize * sizeof(char));
	p_partialResult		= malloc(PARTIAL_RESULT_SIZE * sizeof(int));
	
	CHECK_MEMORY_ALLOCATION(g_chunkBuffer)
	CHECK_MEMORY_ALLOCATION(p_partialResult)
	
	memset_pattern4(p_partialResult, (void *) &temp, PARTIAL_RESULT_SIZE * sizeof(int));
	
	if (g_mpi_rank == 0)
	{
		int i;
		
		p_sendcounts[0]	= g_chunkSize;
		p_displs[0]		= 0;
		
		for (i = 1; i < g_mpi_size; i++)
		{
			p_sendcounts[i] = g_chunkSize;
			p_displs[i] += p_displs[i-1] + g_chunkSize - overlapSize;
		}
		
	}	
	
	return result;
	
}

int Disp_Scatter(Disp_Iterator_Type startIndex)
{
	MPI_Scatterv(g_textBuffer+startIndex, p_sendcounts, p_displs, MPI_CHAR, g_chunkBuffer, g_chunkSize, MPI_CHAR, ROOT, COMM_DUP);
	
	p_startChunkIndex = startIndex + g_mpi_rank * (g_chunkSize - g_overlapSize);
	
	int last = startIndex + g_mpi_size * (g_chunkSize - g_overlapSize);
	
	g_last_iterator = (last > g_textSize ? ITERATOR_END : last);
	
	return g_last_iterator;
}

void Disp_Gather(int **results)
{
	
	MPI_Gather(p_partialResult, PARTIAL_RESULT_SIZE, MPI_INT, g_results, PARTIAL_RESULT_SIZE, MPI_INT, ROOT, COMM_DUP);
	
	if (Disp_Get_Rank() == ROOT)
	{
		int i, j;
		for (i = 0; i < PARTIAL_RESULT_SIZE * g_mpi_size; i++)
		{
			for (j = 0; j < PARTIAL_RESULT_SIZE * g_mpi_size ; j++)
			{
				unsigned int temp;
				if ((unsigned int) g_results[i] < (unsigned int) g_results[j])
				{
					temp = g_results[i];
					g_results[i] = g_results[j];
					g_results[j] = temp;
				}
			}
		}
	}
	
	*results = g_results;

}

void Disp_Add_Result(int index)
{
	static last = 0;
	p_partialResult[last++] = p_startChunkIndex + index;
}

void Disp_Sync_Int(int *value)
{
	MPI_Bcast(value, 1, MPI_INT, ROOT, COMM_DUP);
}

void Disp_Sync_String(char *string, int size)
{
	MPI_Bcast(string, size, MPI_CHAR, ROOT, COMM_DUP);
}

void Disp_Print_Results(int *results)
{
	if (Disp_Get_Rank() == ROOT)
	{
		int i = 0;
		while (results[i++] != -1)
			printf("%i\n", results[i-1]);
	}
}

char * Disp_Get_Chunk(Disp_Iterator_Type iterator)
{
	return g_chunkBuffer;
}

Disp_Iterator_Type Disp_Get_Next_Iterator(Disp_Iterator_Type iterator)
{
	Disp_Scatter(iterator);
	
	return g_last_iterator;
}

Disp_Iterator_Type Disp_Get_Begin_Iterator()
{
	Disp_Scatter(ITERATOR_BEGIN);
	
	return g_last_iterator;
}

Disp_Iterator_Type Disp_Get_End_Iterator(Disp_Iterator_Type iterator)
{
	return ITERATOR_END;
}

int Disp_Get_Chunk_Size()
{
	return g_chunkSize;
}

int Disp_Get_Rank()
{
	return g_mpi_rank;
}

int Disp_Get_Size()
{
	return g_mpi_size;
}

int Disp_Finalize()
{
	free(p_sendcounts);
	free(p_displs);
	
	free(g_chunkBuffer);
	free(p_partialResult);
	
	if (Disp_Get_Rank() == ROOT)
	{
		free(g_textBuffer);
		free(g_results);
	}
	
	return MPI_Comm_free(&COMM_DUP);
}
